Micron Document
NexusPi Git Node

Commit 79cb2d49e816038dfd804f8ea97f47800aefbc68


Parents : d8c9257
Author : James L <jrl290@gmail.com>
Date : 2026-03-14T12:18:37-04:00

Add transport mode notes and config updates

Changes
Diff

diff --git a/BoundaryConfig.h b/BoundaryConfig.h
index c4bc237..2b0bca4 100755
--- a/BoundaryConfig.h
+++ b/BoundaryConfig.h
@@ -1,7 +1,10 @@
// Copyright (C) 2026, Boundary Mode Extension
// Based on microReticulum_Firmware by Mark Qvist
//
-// BoundaryConfig.h — Captive-portal web configuration for Boundary Mode.
+// BoundaryConfig.h — Captive-portal web configuration for the legacy
+// "Boundary Mode" path. This should be renamed to "Transport Mode"
+// together with the rest of the boundary-mode terminology. In this fork,
+// transport/boundary mode is the only intended mode of operation.
// When triggered (first boot with no config, or button hold >5s),
// the device starts a WiFi AP with a web form for all settings:
// WiFi STA credentials, TCP backbone params, LoRa radio params,
@@ -59,6 +62,16 @@ static const char* const BW_OPTIONS_LABELS[] PROGMEM = {
};
static const int BW_OPTIONS_COUNT = sizeof(BW_OPTIONS_HZ) / sizeof(BW_OPTIONS_HZ[0]);
+static uint8_t config_default_display_rotation() {
+ #if BOARD_MODEL == BOARD_LORA32_V2_1 || BOARD_MODEL == BOARD_TBEAM || BOARD_MODEL == BOARD_RAK4631
+ return 0;
+ #elif BOARD_MODEL == BOARD_HELTEC32_V2 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_HELTEC_T114 || BOARD_MODEL == BOARD_TBEAM_S_V1
+ return 1;
+ #else
+ return 3;
+ #endif
+}
+
// ─── HTML Page Generation ────────────────────────────────────────────────────
static void config_send_html() {
@@ -304,6 +317,11 @@ static void config_send_html() {
cur_blank = EEPROM.read(eeprom_addr(ADDR_CONF_DBLK));
}
+ uint8_t cur_rotation = EEPROM.read(eeprom_addr(ADDR_CONF_DROT));
+ if (cur_rotation > 3) {
+ cur_rotation = config_default_display_rotation();
+ }
+
static const uint8_t blank_vals[] = { 0, 1, 5, 10, 30, 60 };
static const char* blank_labels[] = { "Never", "1 minute", "5 minutes", "10 minutes", "30 minutes", "60 minutes" };
static const int blank_count = 6;
@@ -320,6 +338,23 @@ static void config_send_html() {
html += F("</select>");
html += F("<p class='note'>Turn off display after inactivity to save power</p>");
+ html += F("<label>Display Orientation</label><select name='disp_rot'>");
+ html += F("<option value='0'");
+ if (cur_rotation == 0) html += F(" selected");
+ html += F(">Landscape</option>");
+ html += F("<option value='1'");
+ if (cur_rotation == 1) html += F(" selected");
+ html += F(">Portrait</option>");
+ html += F("<option value='2'");
+ if (cur_rotation == 2) html += F(" selected");
+ html += F(">Landscape Flipped</option>");
+ html += F("<option value='3'");
+ if (cur_rotation == 3) html += F(" selected");
+ html += F(">Portrait Flipped</option>");
+ html += F("</select>");
+ html += F("<p class='note'>Choose the orientation that matches your OLED mounting. "
+ "Landscape modes place the two status panes side by side; portrait modes stack them.</p>");
+
// ── Submit ──
html += F(
"<button type='submit'>Save &amp; Reboot</button>"
@@ -353,6 +388,14 @@ static void config_handle_save() {
// Set WiFi mode to STA
EEPROM.write(eeprom_addr(ADDR_CONF_WIFI), WR_WIFI_STA);
+ // Boundary mode always uses DHCP on the STA interface. Clear the legacy
+ // static IP and netmask slots so stale values from older firmware or tools
+ // cannot force a persistent static address.
+ for (int i = 0; i < 4; i++) {
+ EEPROM.write(config_addr(ADDR_CONF_IP + i), 0x00);
+ EEPROM.write(config_addr(ADDR_CONF_NM + i), 0x00);
+ }
+
// ── WiFi enable setting ──
boundary_state.wifi_enabled = (config_server->arg("wifi_en").toInt() == 1);
@@ -370,6 +413,12 @@ static void config_handle_save() {
eeprom_update(eeprom_addr(ADDR_CONF_DBLK), blank_val);
}
+ int display_rotation = config_server->arg("disp_rot").toInt();
+ if (display_rotation < 0 || display_rotation > 3) {
+ display_rotation = config_default_display_rotation();
+ }
+ eeprom_update(eeprom_addr(ADDR_CONF_DROT), (uint8_t)display_rotation);
+
// ── TCP backbone settings ──
boundary_state.tcp_mode = (uint8_t)config_server->arg("tcp_mode").toInt(); // 0=disabled, 1=client
if (boundary_state.tcp_mode > 1) boundary_state.tcp_mode = 0;
@@ -481,6 +530,14 @@ static void config_handle_redirect() {
// ─── Check if config is needed ───────────────────────────────────────────────
bool boundary_needs_config() {
+ // If the RTNode app marker is missing, this node was either never
+ // configured by RTNode or was flashed from a different firmware family
+ // such as stock RNode. Force the portal so RTNode can claim and rewrite
+ // its persisted settings explicitly.
+ if (!boundary_app_marker_valid()) {
+ return true;
+ }
+
// Check if WiFi SSID is configured
char ssid[33];
for (int i = 0; i < 32; i++) {

diff --git a/BoundaryMode.h b/BoundaryMode.h
index c77b987..3934dfd 100755
--- a/BoundaryMode.h
+++ b/BoundaryMode.h
@@ -1,9 +1,11 @@
// Copyright (C) 2026, Boundary Mode Extension
// Based on microReticulum_Firmware by Mark Qvist
//
-// BoundaryMode.h — Configuration and runtime state for the Boundary Mode
-// firmware variant. This header defines the WiFi backbone connection
-// parameters and boundary-specific operational settings.
+// BoundaryMode.h — Configuration and runtime state for the legacy
+// "Boundary Mode" firmware variant. Going forward this should be renamed
+// "Transport Mode". It is the only intended operating mode for this fork;
+// the old multi-mode split is kept only for compatibility while the codebase
+// is being simplified.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -15,8 +17,19 @@
#ifdef BOUNDARY_MODE
+// Compatibility alias for the planned rename from Boundary Mode to
+// Transport Mode. New code should prefer the transport terminology even
+// while the legacy BOUNDARY_MODE compile-time flag still exists.
+#ifndef TRANSPORT_MODE
+#define TRANSPORT_MODE 1
+#endif
+
// ─── Boundary Mode Configuration ────────────────────────────────────────────
//
+// NOTE: "Boundary Mode" is the legacy name. This should be relabeled
+// "Transport Mode" once the remaining non-transport code paths are removed.
+// In practice this is the only supported mode in this firmware branch.
+//
// The boundary node operates with TWO RNS interfaces:
//
// 1. LoRaInterface (MODE_GATEWAY) — radio side, handles LoRa mesh
@@ -72,10 +85,16 @@
#define ADDR_CONF_IFAC_EN 0xD6 // IFAC enable flag (1 byte, 0x73 = enabled)
#define ADDR_CONF_IFAC_NAME 0xD7 // Network name (33 bytes, null-terminated)
#define ADDR_CONF_IFAC_PASS 0xF8 // Passphrase (33 bytes, null-terminated)
-// Total: 0x119 (281 bytes — extends beyond 256-byte CONFIG area into
+#define ADDR_CONF_APP_MARKER0 0x119 // RTNode app marker byte 0
+#define ADDR_CONF_APP_MARKER1 0x11A // RTNode app marker byte 1
+#define ADDR_CONF_APP_VERSION 0x11B // RTNode app config version
+// Total: 0x11C (284 bytes — extends beyond 256-byte CONFIG area into
// unused EEPROM gap; safe on ESP32 where EEPROM starts at 824)
#define BOUNDARY_ENABLE_BYTE 0x73
+#define BOUNDARY_APP_MARKER0 0x52
+#define BOUNDARY_APP_MARKER1 0x54
+#define BOUNDARY_APP_VERSION 0x01
// ─── Boundary Mode Runtime State ─────────────────────────────────────────────
#ifndef BOUNDARY_STATE_DEFINED
@@ -115,6 +134,24 @@ extern BoundaryState boundary_state;
// ─── Boundary Mode EEPROM Load/Save ─────────────────────────────────────────
+inline bool boundary_app_marker_valid() {
+ return EEPROM.read(config_addr(ADDR_CONF_APP_MARKER0)) == BOUNDARY_APP_MARKER0 &&
+ EEPROM.read(config_addr(ADDR_CONF_APP_MARKER1)) == BOUNDARY_APP_MARKER1;
+}
+
+inline bool boundary_app_version_matches() {
+ return boundary_app_marker_valid() &&
+ EEPROM.read(config_addr(ADDR_CONF_APP_VERSION)) == BOUNDARY_APP_VERSION;
+}
+
+inline void boundary_clear_app_marker() {
+ EEPROM.write(config_addr(ADDR_CONF_APP_MARKER0), 0xFF);
+ EEPROM.write(config_addr(ADDR_CONF_APP_MARKER1), 0xFF);
+ EEPROM.write(config_addr(ADDR_CONF_APP_VERSION), 0xFF);
+ EEPROM.write(config_addr(ADDR_CONF_BMODE), 0xFF);
+ EEPROM.commit();
+}
+
inline void boundary_load_config() {
// Check if boundary mode is configured
uint8_t bmode = EEPROM.read(config_addr(ADDR_CONF_BMODE));
@@ -258,6 +295,9 @@ inline void boundary_save_config() {
EEPROM.write(config_addr(ADDR_CONF_IFAC_PASS + i), boundary_state.ifac_passphrase[i]);
}
EEPROM.write(config_addr(ADDR_CONF_IFAC_PASS + 32), 0x00);
+ EEPROM.write(config_addr(ADDR_CONF_APP_MARKER0), BOUNDARY_APP_MARKER0);
+ EEPROM.write(config_addr(ADDR_CONF_APP_MARKER1), BOUNDARY_APP_MARKER1);
+ EEPROM.write(config_addr(ADDR_CONF_APP_VERSION), BOUNDARY_APP_VERSION);
EEPROM.commit();
}

diff --git a/RNode_Firmware.ino b/RNode_Firmware.ino
index 89e5154..00ec5f4 100755
--- a/RNode_Firmware.ino
+++ b/RNode_Firmware.ino
@@ -28,6 +28,9 @@
#include "Utilities.h"
// CBA Boundary Mode
+// NOTE: Boundary Mode is the legacy name. This firmware branch intends to
+// converge on a single Transport Mode, with the BOUNDARY_MODE symbol kept
+// temporarily as a compatibility shim during cleanup.
#ifdef BOUNDARY_MODE
#include "BoundaryMode.h"
#include "TcpInterface.h"
@@ -525,6 +528,7 @@ void setup() {
// Enter config mode if: first boot with no config, OR button-triggered reboot,
// OR bootloop detected
+ bool app_marker_missing = !boundary_app_marker_valid();
bool need_config = boundary_needs_config();
bool config_requested = (boundary_config_request == BOUNDARY_CONFIG_MAGIC);
bool skip_config = (boundary_skip_config == BOUNDARY_SKIP_MAGIC);
@@ -543,6 +547,9 @@ void setup() {
Serial.println("[Boundary] Entering config portal due to bootloop recovery");
} else if (config_requested) {
Serial.println("[Boundary] Config mode requested via button hold");
+ } else if (app_marker_missing) {
+ Serial.println("[Boundary] RTNode app marker missing — previous firmware was not RTNode or config is unclaimed");
+ Serial.println("[Boundary] Starting config portal to migrate settings into RTNode");
} else {
Serial.println("[Boundary] No configuration found — starting config portal");
}
@@ -1734,6 +1741,9 @@ void serial_callback(uint8_t sbyte) {
eeprom_conf_save();
} else if (command == CMD_CONF_DELETE) {
eeprom_conf_delete();
+ #ifdef BOUNDARY_MODE
+ boundary_clear_app_marker();
+ #endif
} else if (command == CMD_FB_EXT) {
#if HAS_DISPLAY == true
if (sbyte == 0xFF) {

diff --git a/Release/rnode_firmware_heltec32v3.bin b/Release/rnode_firmware_heltec32v3.bin
old mode 100644
new mode 100755
index b88f4cd..22d7d56
Binary files a/Release/rnode_firmware_heltec32v3.bin and b/Release/rnode_firmware_heltec32v3.bin differ

diff --git a/Release/rnode_firmware_heltec32v4_boundary.bin b/Release/rnode_firmware_heltec32v4_boundary.bin
index c3073f9..8dd627b 100755
Binary files a/Release/rnode_firmware_heltec32v4_boundary.bin and b/Release/rnode_firmware_heltec32v4_boundary.bin differ

diff --git a/Remote.h b/Remote.h
index d8a0fae..650f2b3 100755
--- a/Remote.h
+++ b/Remote.h
@@ -78,6 +78,13 @@ void wifi_remote_start_ap() {
void wifi_remote_start_sta() {
WiFi.mode(WIFI_STA);
+#ifdef BOUNDARY_MODE
+ // Boundary mode does not expose static STA addressing in its config flow.
+ // Always return the station interface to DHCP so stale legacy EEPROM data
+ // cannot pin the node to an unintended address.
+ WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
+#else
+
uint8_t ip[4]; bool ip_ok = true;
for (uint8_t i = 0; i < 4; i++) { ip[i] = EEPROM.read(config_addr(ADDR_CONF_IP+i)); }
if (ip[0]==0x00 && ip[1]==0x00 && ip[2]==0x00 && ip[3]==0x00) { ip_ok = false; }
@@ -97,6 +104,7 @@ void wifi_remote_start_sta() {
IPAddress dns2(1, 1, 1, 1);
WiFi.config(sta_ip, sta_gw, sta_nm, dns1, dns2);
}
+#endif
delay(100);
if (wr_ssid[0] != 0x00) {

Served by rngit 1.4.2 - Generated in 0.09s